using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Windows.Forms;
using MAPIdotnet;

namespace ShoppingList
{
    public partial class Main : Form
    {
        private static string fullPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName); 
        private static string FILE_NAME = fullPath + "\\ShoppingList.xml";
        public const string MESSAGE_IDENTIFIER = "SListMSG";    //used for sendning/importing SMS

        private Dictionary<string, ShoppingList> lists = new Dictionary<string, ShoppingList>();
        private Comparison<ListViewItem> lastSortMethod = SortByName;

        MAPI mapi;

        AddItems addItemsDialog = new AddItems();
        Share shareDialog = new Share();
        Import importDialog = new Import();

        public Main()
        {
            InitializeComponent();
            addItemsDialog.Owner = this;
            ReadXml();
        }

        /// <summary>
        /// Read all items and lists from xml
        /// </summary>
        private void ReadXml()
        {
            XmlDocument xml = new XmlDocument();
            xml.Load(FILE_NAME);

            lists.Clear();
            cbLists.Items.Clear();

            //load lists
            XmlNodeList xmlLists = xml.DocumentElement.SelectNodes("lists/list");
            foreach (XmlNode node in xmlLists)
            {
                string name = node.Attributes["name"].Value;
                ShoppingList list = new ShoppingList(name);

                //load items in list
                XmlNodeList xmlItems = node.SelectNodes("item");
                foreach (XmlNode itemNode in xmlItems)
                {
                    bool inlist = bool.Parse(itemNode.Attributes["inlist"].Value);
                    int timesCheckedOut = int.Parse(itemNode.Attributes["timescheckedout"].Value);
                    string itemName = itemNode.Attributes["name"].Value;
                    ShoppingItem item = new ShoppingItem(itemName);
                    item.TimesCheckedOut = timesCheckedOut;
                    item.Inlist = inlist;
                    list.Items.Add(itemName, item);
                }
                lists.Add(list.Name, list);
                cbLists.Items.Add(list);
            }

            //set last selected list as the one selected
            XmlNode xmlLastSelectedList = xml.DocumentElement.SelectSingleNode("lists");
            string selectedList = xmlLastSelectedList.Attributes["selectedlist"].Value;
            cbLists.SelectedItem = lists[selectedList];
        }

        /// <summary>
        /// Save all lists to xml file
        /// </summary>
        private void SaveXml()
        {
            Cursor.Current = Cursors.WaitCursor;
            //take a backup of existing file
            try
            {
                File.Delete(FILE_NAME.Replace(".xml", ".bak"));
                File.Move(FILE_NAME, FILE_NAME.Replace(".xml", ".bak"));
            }
            catch (FileNotFoundException) { }   //If no backup file found this is probably the first time we started

            //create xml file
            XmlDocument xml = new XmlDocument();
            xml.PrependChild(xml.CreateXmlDeclaration("1.0", "UTF-8", null));
            xml.AppendChild(xml.CreateElement("shoppinglist"));
            
            //create lists
            XmlElement xmlLists = xml.CreateElement("lists");
            xmlLists.Attributes.Append(xml.CreateAttribute("selectedlist"));
            xmlLists.Attributes["selectedlist"].Value = ((ShoppingList)cbLists.SelectedItem).Name;

            foreach( ShoppingList list in lists.Values)
            {
                XmlElement xmlList = xml.CreateElement("list");
                xmlList.Attributes.Append(xml.CreateAttribute("name"));
                xmlList.Attributes["name"].Value = list.Name;


                //create items
                foreach (ShoppingItem item in list.Items.Values)
                {
                    XmlElement xmlItem = xml.CreateElement("item");
                    xmlItem.Attributes.Append(xml.CreateAttribute("name"));
                    xmlItem.Attributes.Append(xml.CreateAttribute("timescheckedout"));
                    xmlItem.Attributes.Append(xml.CreateAttribute("inlist"));
                    xmlItem.Attributes["name"].Value = item.Name;
                    xmlItem.Attributes["timescheckedout"].Value = item.TimesCheckedOut.ToString();
                    xmlItem.Attributes["inlist"].Value = item.Inlist.ToString();
                    xmlList.AppendChild(xmlItem);
                }
                xmlLists.AppendChild(xmlList);
            }
            xml.DocumentElement.AppendChild(xmlLists);

            xml.Save(FILE_NAME);
            Cursor.Current = Cursors.Default;

        }

        /// <summary>
        /// Called when the user selects a list in the drop down.
        /// Updates the item list for the current list with the items belonging to the selected list.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cbLists_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateItemList();
        }

        /// <summary>
        /// Updates the list of items displayed for a list
        /// </summary>
        private void UpdateItemList()
        {
            lsvListItems.Items.Clear();
            ShoppingList selectedList = (ShoppingList)cbLists.SelectedItem;
            foreach (ShoppingItem item in selectedList.Items.Values)
            {
                if (item.Inlist)
                {
                    ListViewItem lvi = new ListViewItem(item.Name);
                    lvi.Tag = item;
                    lsvListItems.Items.Add(lvi);
                }
            }
            SortListView(lsvListItems, lastSortMethod);
        }

        /// <summary>
        /// Called when the user has selected item(s) to be removed from the current list.
        /// Saves changes to xml.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void miRemove_Click(object sender, EventArgs e)
        {
            ShoppingList currentList = (ShoppingList)cbLists.SelectedItem;
            foreach (int index in lsvListItems.SelectedIndices)
            {
                ShoppingItem itemToRemove = (ShoppingItem)lsvListItems.Items[index].Tag;
                currentList.Items.Remove(itemToRemove.Name);
            }
            UpdateItemList();
            SaveXml();
        }

        /// <summary>
        /// Called when the popup contect menu opens. Enables/disables the correct menu items
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ListContectMenu_Popup(object sender, EventArgs e)
        {
            if (lsvListItems.SelectedIndices.Count > 0) miRemove.Enabled = true;
            else miRemove.Enabled = false;
        }

        /// <summary>
        /// Displays the dialog that allows the user to add items to the current list
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void miAdditems_Click(object sender, EventArgs e)
        {
            ShoppingList currentList = (ShoppingList)cbLists.SelectedItem;
            //updates currentList
            Dictionary<string, ShoppingItem> itemsToRemove = addItemsDialog.ShowDialog(currentList);

            if (addItemsDialog.DialogResult == DialogResult.OK)
            {
                //delete any items
                foreach (ShoppingItem item in itemsToRemove.Values)
                {
                    foreach(ShoppingList list in lists.Values)
                    {
                        list.Items.Remove(item.Name);
                    }
                }
                UpdateItemList();
                SaveXml();
            }
        }

        /// <summary>
        /// Called when the user presses the exit button. Closes the form.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void miExit_Click(object sender, EventArgs e)
        {
            DialogResult res = MessageBox.Show("Really Exit now?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
            if (res == DialogResult.Yes)
            {
                Close();
            }
        }

        /// <summary>
        /// Called when the user clicks the checkout button.
        /// Removes all checked items from the list and updates the timesCheckedOut property
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void miCheckout_Click(object sender, EventArgs e)
        {
            //if no items are checked, can not checkout
            bool anyChecked = false;
            foreach (ListViewItem lvItem in lsvListItems.Items)
            {
                if (lvItem.Checked)
                {
                    anyChecked = true;
                    break;
                }
            }
            if (!anyChecked)
            {
                MessageBox.Show("At least one item must be selected to checkout!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand, MessageBoxDefaultButton.Button1);
                return;
            }

            //show warning dialog
            DialogResult res = MessageBox.Show("Really Checkout now?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
            if (res == DialogResult.Yes)
            {
                ShoppingList currentList = (ShoppingList)cbLists.SelectedItem;
                foreach (ListViewItem lvItem in lsvListItems.Items)
                {
                    //if item in list is checked, update count and item
                    if (lvItem.Checked)
                    {
                        ShoppingItem item = (ShoppingItem)lvItem.Tag;
                        item.TimesCheckedOut++;
                        item.Inlist = false;
                    }
                }
                UpdateItemList();
                SaveXml();
            }
        }

        /// <summary>
        /// Called when the user choses the delete list button.
        /// If more than one list is available deletes the list, but not the items belonging to list.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void miDelete_Click(object sender, EventArgs e)
        {
            //make sure there still is at least one list
            if( lists.Count <= 1 )
            {
                MessageBox.Show("Can not delete last list.");
                return;
            }
            //delete list
            DialogResult res = MessageBox.Show("Really delete current list?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2);
            if (res == DialogResult.Yes)
            {
                ShoppingList currentList = (ShoppingList)cbLists.SelectedItem;
                lists.Remove(currentList.Name);
                cbLists.Items.RemoveAt(cbLists.SelectedIndex);
                cbLists.SelectedIndex = 0;
            }

        }

        /// <summary>
        /// Called when the user clicks the new list button.
        /// Creates a new empty list with the name in the text field.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void miNewList_Click(object sender, EventArgs e)
        {
            string listName = InputBox.Show("Enter name for list", "New list", "");
            //TODO: Add errormessage if list exists
            if (listName != string.Empty && !lists.ContainsKey(listName) )
            {
                ShoppingList newList = new ShoppingList(listName);
                lists.Add(newList.Name, newList);
                cbLists.Items.Add(newList);
                cbLists.SelectedItem = newList;
                UpdateItemList();
                SaveXml();
            }
        }

        private void lnkSortChecked_Click(object sender, EventArgs e)
        {
            SortListView(lsvListItems, SortByChecked);
            lastSortMethod = SortByChecked;
        }

        private void lnkSortName_Click(object sender, EventArgs e)
        {
            SortListView(lsvListItems, SortByName);
            lastSortMethod = SortByName;
        }

        /// <summary>
        /// Sorts a list view using the specified comparison method
        /// </summary>
        /// <param name="listViewToSort"></param>
        /// <param name="comparison"></param>
        public static void SortListView(ListView listViewToSort, Comparison<ListViewItem> comparison)
        {
            int numItems = listViewToSort.Items.Count;
            List<ListViewItem> sortArray = new List<ListViewItem>();
            listViewToSort.BeginUpdate();
            foreach (ListViewItem lvi in listViewToSort.Items)
            {
                sortArray.Add(lvi);
            }
            sortArray.Sort(comparison);
            listViewToSort.Items.Clear();
            foreach (ListViewItem lvi in sortArray)
            {
                listViewToSort.Items.Add(lvi);
            }

            listViewToSort.EndUpdate();
        }

        public static int SortByChecked(ListViewItem x, ListViewItem y)
        {
            if( x.Checked && !y.Checked) return 1;
            if( y.Checked && !x.Checked) return -1;
            return x.Text.CompareTo(y.Text);
        }

        public static int SortByName(ListViewItem x, ListViewItem y)
        {
            return x.Text.CompareTo(y.Text);
        }

        /// <summary>
        /// Sort by times checked out (mort checked out comes first).
        /// The Tag property MUST be a ShoppingItem!
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public static int SortByCheckout(ListViewItem x, ListViewItem y)
        {
            ShoppingItem s1 = (ShoppingItem)x.Tag;
            ShoppingItem s2 = (ShoppingItem)y.Tag;

            return s2.TimesCheckedOut - s1.TimesCheckedOut;
        }

        private void miShare_Click(object sender, EventArgs e)
        {
            ShoppingList selectedList = (ShoppingList)cbLists.SelectedItem;
            shareDialog.ShowDialog(selectedList);
        }

        private void miImportSMS_Click(object sender, EventArgs e)
        {
            Cursor.Current = Cursors.WaitCursor;
            if( mapi == null) mapi = new MAPI();
            
            //find sms store
            IMAPIMsgStore smsStore = null;
            foreach (IMAPIMsgStore store in mapi.MesssageStores)
            {
                if (store.DisplayName.Equals("SMS"))
                {
                    smsStore = store;
                    break;
                }
            }

            //find last message with list information
            if (smsStore != null)
            {
                IMAPIFolder inbox = smsStore.ReceiveFolder;
                inbox.SortMessagesByDeliveryTime(TableSortOrder.TABLE_SORT_DESCEND);
                bool foundMessage = false;
                foreach (IMAPIMessage message in inbox.GetNextMessages((int)inbox.NumSubItems))
                {
                    //show import dialog if message found
                    if (message.Subject.StartsWith(MESSAGE_IDENTIFIER))
                    {
                        Cursor.Current = Cursors.Default;
                        lblStatus.Text = "Found shopping list";
                        foundMessage = true;
                        string[] msgItems = message.Subject.Split('|');
                        //add selected items to current list
                        if (DialogResult.OK == importDialog.ShowDialog(msgItems, (ShoppingList)cbLists.SelectedItem))
                        {
                            ShoppingList currentList =  (ShoppingList)cbLists.SelectedItem;
                            //add new items to current list and list of all items, unless an item with the name already exists, then use that one
                            foreach( ShoppingItem importItem in importDialog.SelectedItems)
                            {

                                ShoppingItem exitst = currentList.Items[importItem.Name];
                                //use existing item
                                if (exitst != null)
                                {
                                    currentList.Items.Add(exitst.Name, exitst);
                                }
                                //use the remote item
                                else
                                {
                                    currentList.Items.Add(importItem.Name, importItem);
                                }
                            }
                            UpdateItemList();
                            SaveXml();
                        }
                        break;
                    }
                }
                if (!foundMessage) lblStatus.Text = "No shopping list found in SMS";
            }
            else lblStatus.Text = "No SMS inbox found";
            Cursor.Current = Cursors.Default;

        }

    }

}